import numpy as np
import math as m

class PSI:
    def __init__(self, DecisionMatrix, q, maxminArray):
        '''
        :param DecisionMatrix: 决策矩阵
        :param q: q值
        :param maxminArray: :最大最小值
        '''
        self.matrix = DecisionMatrix
        self.q = q
        self.maxminArray = maxminArray

    # 加法
    def addition(self,Ivq1,Ivq2,q):
        '''
        加法
        :param Ivq1:区间值
        :param Ivq2:区间值
        :param q:q值
        :return:
        '''
        a,b,c,d=Ivq1[0][0],Ivq1[0][1],Ivq1[1][0],Ivq1[1][1]
        e,f,g,h=Ivq2[0][0],Ivq2[0][1],Ivq2[1][0],Ivq2[1][1]
        u1=a**q+e**q-(a**q)*(e**q)
        u2=b**q+f**q-(b**q)*(f**q)
        v1=c*g
        v2=h*d
        return ([u1,u2],[v1,v2])

    # 减法
    def subtraction(self,Ivq1,Ivq2,q):
        '''
        减法
        :param Ivq1:区间值
        :param Ivq2:区间值
        :param q:q值
        :return:
        '''
        a,b,c,d=Ivq1[0][0],Ivq1[0][1],Ivq1[1][0],Ivq1[1][1]
        e,f,g,h=Ivq2[0][0],Ivq2[0][1],Ivq2[1][0],Ivq2[1][1]
        u1=a*g
        u2=b*h
        v1=(c**q+e**q-(c**q)*(e**q))**(1/q)
        v2=(d**q+f**q-(d**q)*(f**q))**(1/q)
        return ([u1, u2],[v1, v2])

    # 乘法
    def multiplication(self,Ivq1,Ivq2,q):
        '''
        乘法
        :param Ivq1:区间值
        :param Ivq2:区间值
        :param q:q值
        :return:
        '''
        a, b, c, d = Ivq1[0][0], Ivq1[0][1], Ivq1[1][0], Ivq1[1][1]
        e, f, g, h = Ivq2[0][0], Ivq2[0][1], Ivq2[1][0], Ivq2[1][1]
        u1 = a * e
        u2 = b * f
        v1 = (c ** q + g ** q - (c ** q) * (g ** q)) ** (1 / q)
        v2 = (d ** q + h ** q - (d ** q) * (h ** q)) ** (1 / q)
        return ([u1, u2], [v1, v2])

    # 除法
    def division(self,Ivq1,Ivq2,q):
        '''
        除法
        :param Ivq1:区间值
        :param Ivq2:区间值
        :param q:q值
        :return:
        '''
        a, b, c, d = Ivq1[0][0], Ivq1[0][1], Ivq1[1][0], Ivq1[1][1]
        e, f, g, h = Ivq2[0][0], Ivq2[0][1], Ivq2[1][0], Ivq2[1][1]
        u1 = (a ** q + g ** q - (a ** q) * (g ** q)) ** (1 / q)
        u2 = (b ** q + h ** q - (b ** q) * (h ** q)) ** (1 / q)
        v1 = c*e
        v2 = d*f
        return ([u1, u2], [v1, v2])

    # 次方运算
    def Exponentiation(self,Ivq1,q,n):
        '''
        次方运算
        :param Ivq1:
        :param q:
        :param n:开多少次方
        :return:
        '''
        a, b, c, d = Ivq1[0][0], Ivq1[0][1], Ivq1[1][0], Ivq1[1][1]
        u1 = a ** n
        u2 = b ** n
        v1 =(1-((1-(c**q))**n))**(1/q)
        v2 =(1-((1-(d**q))**n))**(1/q)
        return ([u1, u2], [v1, v2])

    # 数乘运算
    def Numbermulti(self,Ivq,n,q):
        '''
        数乘
        :param Ivq: 区间值
        :param n: 实数
        :param q:
        :return:
        '''
        a, b, c, d = Ivq[0][0], Ivq[0][1], Ivq[1][0], Ivq[1][1]
        u1 = (1-((1-a ** q)**n)) ** (1/q)
        u2 = (1-((1-b ** q)**n)) ** (1/q)
        v1 = c **n
        v2 = d **n
        return ([u1, u2], [v1, v2])

    def getPosNegPoint(self, NormalizedMatrix):
        '''
        求正负理想解
        :param NormalizedMatrix: 区间值矩阵
        :param q: q值
        :return: 传入区间值矩阵的贴近度矩阵
        '''
        Row, Column = len(NormalizedMatrix), len(NormalizedMatrix[0])
        for i in range(Row):
            Positiveline, Negativeline = [], []
            for j in range(Column):
                ##求理想点
                Positiveidu1 = np.max((((NormalizedMatrix[:, j])[:, 0])[:, 0]))
                Positiveidu2 = np.max((((NormalizedMatrix[:, j])[:, 0])[:, 1]))
                Positiveidv1 = np.min((((NormalizedMatrix[:, j])[:, 1])[:, 0]))
                Positiveidv2 = np.min((((NormalizedMatrix[:, j])[:, 1])[:, 1]))
                Negativeidu1 = np.min((((NormalizedMatrix[:, j])[:, 0])[:, 0]))
                Negativeidu2 = np.min((((NormalizedMatrix[:, j])[:, 0])[:, 1]))
                Negativeidv1 = np.max((((NormalizedMatrix[:, j])[:, 1])[:, 0]))
                Negativeidv2 = np.max((((NormalizedMatrix[:, j])[:, 1])[:, 1]))
                Positiveline.append(([Positiveidu1, Positiveidu2], [Positiveidv1, Positiveidv2]))
                Negativeline.append(([Negativeidu1, Negativeidu2], [Negativeidv1, Negativeidv2]))

        return Positiveline, Negativeline

    def NormalizedMatrix(self, Matrix, Positiveline, Negativeline):
        '''
        规范化矩阵
        :param Matrix:决策矩阵
        :param Positiveline:最大值数组
        :param Negativeline:最小值数组
        '''
        ROW,COL = len(Matrix),len(Matrix[0])
        for row in range(ROW):
            for col in range(COL):
                if self.maxminArray[col]:
                    Matrix[row][col] = self.division(Matrix[row][col], Positiveline[col], self.q)
                else:
                    Matrix[row][col] = self.division(Negativeline[col], Matrix[row][col], self.q)
        return Matrix
    
    def MeanValueCol(self, NormalizedMatrix):
        '''
        计算列均值
        :param NormalizedMatrix: 规范化矩阵
        '''
        MeanValueCol = np.sum(NormalizedMatrix,axis=0) / len(NormalizedMatrix)
        ROW,COL = len(NormalizedMatrix),len(NormalizedMatrix[0])
        MeanValueCol = []
        for col in range(COL):
            MeanValueCell = NormalizedMatrix[0][col]
            for row in range(1, ROW):
                MeanValueCell = self.addition(MeanValueCell, NormalizedMatrix[row][col], self.q)
            MeanValueCol.append(self.Numbermulti(MeanValueCell, 1/ROW, self.q))
        return MeanValueCol

    def VariationValueMatrix(self, NormalizedMatrix, MeanValueCol):
        '''
        计算偏好变化值
        :param NormalizedMatrix: 规范化矩阵
        :param MeanValueCol: 列均值
        '''
        ROW,COL = len(NormalizedMatrix),len(NormalizedMatrix[0])
        VariationValueMatrix = np.array(self.matrix)
        for row in range(ROW):
            for col in range(COL):
                VariationValueMatrix[row][col] = self.Exponentiation(self.subtraction(NormalizedMatrix[row][col], MeanValueCol[col], self.q), self.q, 2)
        return VariationValueMatrix

    def getMarginalUtilityScore(self,NormalizedMatrix):
        '''
        获取边际效用得分
        :param NormalizedMatrix:规范化矩阵
        :return:
        '''
        ScoreMatrix=[]
        for Row in range(len(NormalizedMatrix)):
            Scoreline=[]
            for Column in range(len(NormalizedMatrix[0])):
                t=self.getscore(NormalizedMatrix[Row][Column])
                Scoreline.append(t)
            ScoreMatrix.append(Scoreline)
        return ScoreMatrix

    def getscore(self,IVq):
        '''
        我们提出的得分函数
        :param IVq:区间值
        :param q:q值
        :return:
        '''
        a = IVq[0][0]
        b = IVq[0][1]
        c = IVq[1][0]
        d = IVq[1][1]
        return (m.log(a + b + c + d + 1) + (b - a) + (d - c) ** 2 + (a - c + b - d) * m.log(3) / 2) / (2 * m.log(3))

    def DeviationsValueCol(self,VariationValueMatrix):
        '''
        计算属性值偏差
        :param VariationValueMatrix: 偏好变化值矩阵
        '''
        DeviationsValueCol = 1 - np.sum(VariationValueMatrix,axis=0)
        return DeviationsValueCol

    def CriteriaWeights(self, DeviationsValueCol):
        '''
        计算属性权重
        :param DeviationsValueCol: 属性值偏差
        '''
        CriteriaWeights = []
        for col in range(len(DeviationsValueCol)):
            CriteriaWeights.append(DeviationsValueCol[col] / sum(DeviationsValueCol))
        return CriteriaWeights

    def getFinalUtilityScore(self, NormalizedMatrix, CriteriaWeights):
        '''
        获取最终效用得分
        :param NormalizedMatrix: 规范化矩阵
        :param CriteriaWeights: 权重
        '''
        U = []
        ROW,COL = len(NormalizedMatrix),len(NormalizedMatrix[0])
        for row in range(ROW):
            u0 = 0
            for col in range(COL):
                u0 += NormalizedMatrix[row][col] * CriteriaWeights[col]
            U.append(u0)
        return U
    
    def getResult(self):
        '''
        最终结果
        '''
        # Step 3: Determine the normalized decision matrix 
        Positiveline, Negativeline=self.getPosNegPoint(self.matrix)
        NormalizedMatrix = self.NormalizedMatrix(self.matrix, Positiveline, Negativeline)
        # # Step 4: Determine the mean values of normalized performances
        MeanValueCol = self.MeanValueCol(NormalizedMatrix)
        # # Step 5: Determine the values of the variation of preferences 
        VariationValueMatrix = self.VariationValueMatrix(NormalizedMatrix, MeanValueCol)
        # # Step 6: Determine the deviations of the value of the preference
        ScoreMatrix=self.getMarginalUtilityScore(VariationValueMatrix)
        DeviationsValueCol = self.DeviationsValueCol(ScoreMatrix)
        # # Step 7: Determine the criteria weights
        CriteriaWeights = self.CriteriaWeights(DeviationsValueCol)
        # # Step 8: Determine the preference selection index of alternatives
        U=self.getFinalUtilityScore(self.getMarginalUtilityScore(NormalizedMatrix), CriteriaWeights)
        U=[i/sum(U) for i in U]
        return U

if __name__ == "__main__":
    Matrix = np.array([[([0.58, 0.75], [0.1, 0.2]),  ([0.7, 0.78], [0.2, 0.3]),  ([0.5, 0.78], [0.1, 0.2]), ([0.65, 0.7], [0.3, 0.35]), ([0.4, 0.5], [0.5, 0.6])],
                       [([0.5, 0.6], [0.1, 0.2]),    ([0.75, 0.78], [0.2, 0.3]), ([0.6, 0.78], [0.1, 0.15]),([0.7, 0.8], [0.2, 0.3]),   ([0.5, 0.6], [0.4, 0.5])],
                       [([0.5, 0.78], [0.1, 0.2]),   ([0.6, 0.7], [0.3, 0.4]),   ([0.7, 0.78], [0.2, 0.3]), ([0.6, 0.7], [0.3, 0.4]),   ([0.35, 0.45], [0.5, 0.65])],
                       [([0.75, 0.78], [0.2, 0.3]), ([0.6, 0.75], [0.2, 0.3]),  ([0.7, 0.78], [0.2, 0.3]), ([0.55, 0.65], [0.3, 0.4]), ([0.35, 0.4], [0.6, 0.65])],
                       [([0.6, 0.75], [0.3, 0.4]),   ([0.5, 0.6], [0.4, 0.5]),   ([0.6, 0.7], [0.3, 0.4]),  ([0.5, 0.6], [0.4, 0.5]),   ([0.1, 0.2], [0.4, 0.5])]])

    # 1为最大值 0为最小值
    maxminArray = [0, 0, 0, 0, 0]

    maut = PSI(Matrix, 3, maxminArray)

    result = maut.getResult()
    print(result)
    # print(f'U={np.round(result,4)}')

